/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package de.tor.tribes.reportserver.util;

import com.vaadin.ui.Alignment;
import com.vaadin.ui.Component;
import com.vaadin.ui.GridLayout;
import java.io.PrintStream;
import java.text.SimpleDateFormat;
import org.apache.commons.lang.StringUtils;

/**
 *
 * @author Torridity
 */
public class UIUtil {

    public static class GridLayoutBuilder {

        /**
         * The default alignment of a component within a cell
         */
        private static final Alignment DEFAULT_ALIGNMENT = Alignment.TOP_LEFT;
        /**
         * A representation used for empty cells
         */
        private static final String EMPTY_CELL = "||||||||||";
        /**
         * The final layout
         */
        private GridLayout layout;
        /**
         * The debugging representation of the layout
         */
        private String[][] assignedFields;
        /**
         * The number of columns
         */
        private int columns;
        /**
         * The number of rows
         */
        private int rows;

        /**
         * The default constructor initializing a layout builder with the
         * provided dimensions.
         *
         * @param pColumns The number of columns
         * @param pRows The number of rows
         */
        public GridLayoutBuilder(int pColumns, int pRows) {
            columns = pColumns;
            rows = pRows;
            layout = new GridLayout(columns, rows);
            assignedFields = new String[columns][rows];

            for (int i = 0; i < columns; i++) {
                for (int j = 0; j < rows; j++) {
                    assignedFields[i][j] = EMPTY_CELL;
                }
            }
        }

        /**
         * The constructor taking an existing GridLayout. This layout is parsed
         * and may be filled (only for cells which are not assigned yet) or
         * printed out for debugging purposes.
         *
         * @param pLayout The existing layout
         */
        public GridLayoutBuilder(GridLayout pLayout) {
            columns = pLayout.getColumns();
            rows = pLayout.getRows();
            layout = new GridLayout(columns, rows);
            assignedFields = new String[columns][rows];

            for (int i = 0; i < columns; i++) {
                for (int j = 0; j < rows; j++) {
                    assignedFields[i][j] = EMPTY_CELL;
                }
            }
            for (int i = 0; i < columns; i++) {
                for (int j = 0; j < rows; j++) {
                    Component com = pLayout.getComponent(i, j);
                    if (com != null) {
                        assignedFields[i][j] = getComponentIdentifier(com);
                    }
                }
            }
            layout = pLayout;
        }

        /**
         * Add a component filling row and column defined by pColumn and pRow
         * till the end of the grid. The default default alignment is used.
         *
         * @param pComponent The component, which must not be 'null'
         * @param pColumn The target column
         * @param pRow The target row
         *
         * @return GridLayoutBuilder.this
         */
        public GridLayoutBuilder fill(Component pComponent, int pColumn, int pRow) {
            return addComponent(pComponent, null, pColumn, pRow, columns - pColumn, rows - pRow);
        }

        /**
         * Add a component filling row and column defined by pColumn and pRow
         * till the end of the grid.
         *
         * @param pComponent The component, which must not be 'null'
         * @param pAlignment The component alignment within the cell
         * @param pColumn The target column
         * @param pRow The target row
         * @param pHeight The height in cells
         *
         * @return GridLayoutBuilder.this
         */
        public GridLayoutBuilder fill(Component pComponent, Alignment pAlignment, int pColumn, int pRow) {
            return addComponent(pComponent, pAlignment, pColumn, pRow, columns - pColumn, rows - pRow);
        }

        /**
         * Add a component filling the column defined by pColumn till the end of
         * the grid. The default default alignment is used.
         *
         * @param pComponent The component, which must not be 'null'
         * @param pColumn The target column
         * @param pRow The target row
         * @param pHeight The height in cells
         *
         * @return GridLayoutBuilder.this
         */
        public GridLayoutBuilder fillColumn(Component pComponent, int pColumn, int pRow, int pWidth) {
            return addComponent(pComponent, null, pColumn, pRow, pWidth, rows - pRow);
        }

        /**
         * Add a component filling the column defined by pColumn till the end of
         * the grid.
         *
         * @param pComponent The component, which must not be 'null'
         * @param pAlignment The component alignment within the cell
         * @param pColumn The target column
         * @param pRow The target row
         * @param pHeight The height in cells
         *
         * @return GridLayoutBuilder.this
         */
        public GridLayoutBuilder fillColumn(Component pComponent, Alignment pAlignment, int pColumn, int pRow, int pWidth) {
            return addComponent(pComponent, pAlignment, pColumn, pRow, pWidth, rows - pRow);
        }

        /**
         * Add a component filling the row defined by pRow till the end of the
         * grid. The default default alignment is used.
         *
         * @param pComponent The component, which must not be 'null'
         * @param pColumn The target column
         * @param pRow The target row
         * @param pHeight The height in cells
         *
         * @return GridLayoutBuilder.this
         */
        public GridLayoutBuilder fillRow(Component pComponent, int pColumn, int pRow, int pHeight) {
            return addComponent(pComponent, null, pColumn, pRow, columns - pColumn, pHeight);
        }

        /**
         * Add a component filling the row defined by pRow till the end of the
         * grid.
         *
         * @param pComponent The component, which must not be 'null'
         * @param pAlignment The component alignment within the cell
         * @param pColumn The target column
         * @param pRow The target row
         * @param pHeight The height in cells
         *
         * @return GridLayoutBuilder.this
         */
        public GridLayoutBuilder fillRow(Component pComponent, Alignment pAlignment, int pColumn, int pRow, int pHeight) {
            return addComponent(pComponent, pAlignment, pColumn, pRow, columns - pColumn, pHeight);
        }

        /**
         * Add a component with the size 1x1 using the default alignment.
         *
         * @param pComponent The component, which must not be 'null'
         * @param pColumn The target column
         * @param pRow The target row
         *
         * @return GridLayoutBuilder.this
         */
        public GridLayoutBuilder addComponent(Component pComponent, int pColumn, int pRow) {
            return addComponent(pComponent, null, pColumn, pRow, 1, 1);
        }

        /**
         * Add a component using the default alignment.
         *
         * @param pComponent The component, which must not be 'null'
         * @param pColumn The target column
         * @param pRow The target row
         * @param pWidth The width in cells
         * @param pHeight The height in cells
         *
         * @return GridLayoutBuilder.this
         */
        public GridLayoutBuilder addComponent(Component pComponent, int pColumn, int pRow, int pWidth, int pHeight) {
            return addComponent(pComponent, null, pColumn, pRow, pWidth, pHeight);
        }

        /**
         * Add a new component to the layout. This method may throw an
         * IllegalArgumentException if pComponent is null or an
         * IndexOutOfBoundsException if the component location exceeds the
         * layout grid. Furthermore it is checked, whether to component overlaps
         * another component added before. If this is the case, an
         * IllegalArgumentException is throws and the current layout is printed
         * to StdErr. If everything works fine, the component is added to the
         * layout.
         *
         * @param pComponent The component, which must not be 'null'
         * @param pAlignment The component alignment within the cell. The
         * default alignment is MIDDLE_CENTER.
         * @param pColumn The target column
         * @param pRow The target row
         * @param pWidth The width in cells
         * @param pHeight The height in cells
         *
         * @return GridLayoutBuilder.this
         */
        public GridLayoutBuilder addComponent(Component pComponent, Alignment pAlignment, int pColumn, int pRow, int pWidth, int pHeight) {
            if (pComponent == null) {
                throw new IllegalArgumentException("Argument pComponent must not be null");
            }

            Alignment componentAlignment = pAlignment;
            if (componentAlignment == null) {
                componentAlignment = DEFAULT_ALIGNMENT;
            }

            if (pColumn + pWidth > columns || pRow + pHeight > rows) {
                printLayoutToStdErr();
                throw new IndexOutOfBoundsException("Failed to add component " + getComponentIdentifier(pComponent) + ", grid too small.");
            }
            for (int i = pColumn; i < pColumn + pWidth; i++) {
                for (int j = pRow; j < pRow + pHeight; j++) {
                    if (assignedFields[i][j] == null) {
                        printLayoutToStdErr();
                        throw new IllegalArgumentException("Failed to add component " + getComponentIdentifier(pComponent) + ", cell [" + i + "][" + j + "] for unknown reason.");
                    }
                    if (!assignedFields[i][j].equals(EMPTY_CELL)) {
                        printLayoutToStdErr();
                        throw new IllegalArgumentException("Failed to add component " + getComponentIdentifier(pComponent) + ", cell [" + i + "][" + j + "] already assigned.");
                    }
                    assignedFields[i][j] = getComponentIdentifier(pComponent);
                }
            }
            layout.addComponent(pComponent, pColumn, pRow, pColumn + pWidth - 1, pRow + pHeight - 1);
            layout.setComponentAlignment(pComponent, componentAlignment);
            return this;
        }

        /**
         * Get the component identifier. This might be the debug Id, the caption
         * or the item's toString() result, depending on which of these values
         * is not null. The identifier is used for the debug output via
         * printLayout().
         *
         * @param The component
         *
         * @return The identifiert
         */
        private String getComponentIdentifier(Component pComponent) {
            String identifier = pComponent.getDebugId();
            if (identifier == null) {
                identifier = pComponent.getCaption();
                if (identifier == null) {
                    identifier = "#" + pComponent.hashCode();
                }
            }
            return identifier;
        }

        /**
         * Returns the final layout with all components
         *
         * @return GridLayoutBuilder.this
         */
        public GridLayout getLayout() {
            return layout;
        }

        /**
         * Print the current layout to StdErr. This method is for internal use
         * only.
         */
        private void printLayoutToStdErr() {
            printLayout(System.err);
        }

        /**
         * Internal helper method to redirect to debug output to StdOut or
         * StdErr.
         */
        private GridLayoutBuilder printLayout(PrintStream pOut) {
            StringBuilder b = new StringBuilder();

            StringBuilder hr = new StringBuilder();

            for (int i = 0; i < columns * 10 + 4; i++) {
                hr.append("-");
            }
            hr.append("\n");

            b.append(hr.toString());

            for (int j = 0; j < rows; j++) {
                b.append("|");
                for (int i = 0; i < columns; i++) {
                    b.append(StringUtils.center(StringUtils.substring(assignedFields[i][j], 0, 10), 10)).append("|");
                }
                b.append("\n").append(hr.toString());
            }

            pOut.println(b.toString());
            return this;
        }

        /**
         * Print the current layout to StdOut. This method is intended to be
         * used for debugging purposes.
         *
         * @return GridLayoutBuilder.this
         */
        public GridLayoutBuilder printLayout() {
            return printLayout(System.out);
        }
    }

    public static void main(String[] args) throws Exception {
        System.out.println(new SimpleDateFormat("MMM dd, yyyy HH:mm:ss").parse("Jan 12, 2013 10:51:50"));
    }
}
